#include "TcpConnection.h"
#include "Logger.h"
#include "Socket.h"
#include "Channel.h"
#include "EventLoop.h"
#include <functional>
#include <errno.h>
#include <sys/socket.h>
#include <memory>
#include <string>

static EventLoop* CheckLoopNotNull(EventLoop *loop) {
    if (loop == nullptr) {
        LOG_FATAL("tcpconnectionloop is nullptr\n");
    }
    return loop;
}

TcpConnection::TcpConnection( EventLoop *loop,
                        const std::string& nameArg,
                        int sockfd, //接受客户端连接以后的connfd
                        const InetAddress& localAddr,
                        const InetAddress& peerAddr) 
    : loop_(CheckLoopNotNull(loop))
    , name_(nameArg)
    , state_(kConnecting)
    , reading_(true)
    , socket_(new Socket(sockfd))
    , channel_(new Channel(loop, sockfd))
    , localAddr_(localAddr)
    , peerAddr_(peerAddr)
    , highWaterMark_(64 * 1024 * 1024)
{
// 给channel设置相应的回调函数
    channel_->setReadCallback(
        std::bind(&TcpConnection::handleRead, this, std::placeholders::_1)
    );

    channel_->setWriteCallback(
        std::bind(&TcpConnection::handleWrite, this)
    );

    channel_->setCloseCallback(
        std::bind(&TcpConnection::handleClose, this)
    );

    channel_->setErrorCallback(
        std::bind(&TcpConnection::handleError, this)
    );

    socket_->setKeepAlive(true); // 开启保活机制
}


TcpConnection::~TcpConnection() 
{
    LOG_INFO("TcpConnection::~TcpConnection()\n");
}



void TcpConnection::send(const std::string& buf) 
{
    if (state_ == kConnected)
    {
        if (loop_->isInLoopThread())
        {
            sendInLoop(buf.c_str(), buf.size());
        }
        else
        {
            loop_->runInLoop(std::bind(&TcpConnection::sendInLoop, this, buf.c_str(), buf.size()));
        }
    }
}

/*
发送数据  应用写的快，而内核发送数据慢 需要把待发送数据写入缓冲区 而且设置水位回调
*/
void TcpConnection::sendInLoop(const void *data, size_t len)
{
    ssize_t nwrote = 0;
    size_t remaining = len;
    bool faultError = false;

    //调用了connection的shutdown
    if (state_ == kDisconnected) {
        LOG_ERROR("TcpConnection::sendInLoop error \n");
        return ;
    }
    
    //表示channel第一次开始写数据， 而且缓冲区没有待发送数据
    if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
    {
        nwrote = ::write(channel_->fd(), data, len);
        if (nwrote > 0)
        {
            remaining = len - nwrote;
            if (remaining == 0 && writeCompleteCallback_) 
            // 一次性发送完了,就不要给channel设置epollout事件了
            {
                loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
            }
           
        }
        else // nwrote < 0
        {
            nwrote = 0;
            if (errno != EWOULDBLOCK)
            {
                LOG_ERROR("send in loop error");
                if (errno == EPIPE || errno == ECONNRESET)
                {
                    faultError = true;
                }
            }
        }
    }
///////////////
    if (!faultError && remaining > 0) 
    //说明没有把数据全部发送出去，剩余数据需要保存在缓冲区，然后channel注册epollout事件，
    //poller发现tcp缓冲区有空间，会通知channel调用writeCallback回调方法
    //也就是调用方法把发送缓冲区中数据全部发送完成
    {
        //目前发送缓冲区剩余发送数据的长度
        size_t oldLen = outputBuffer_.readableBytes();
        if (oldLen + remaining >= highWaterMark_ && oldLen < highWaterMark_ && highWaterMarkCallback_)
        {
            
            loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
        }
        outputBuffer_.append((char*)data + nwrote, remaining);
        if (!channel_->isWriting())
        {
            channel_->enableWriting(); //让poller通知channel把剩余数据发送出去
        }
    }
///////////////
}


// 关闭当前连接
void TcpConnection::shutdown()
{
    if (state_ == kConnected)
    {
        setState(kDisconnecting);
        loop_->runInLoop(std::bind(&TcpConnection::shutdownInLoop, this));
    }
}

void TcpConnection::shutdownInLoop()
{
    if (!channel_->isWriting()) //说明outputBuffer数据发送完了
    {
        socket_->shutdownWrite(); //关闭写端————————触发EPOLLHUP就会调用closeCallback
    }
}

void TcpConnection::connectEstablished() //连接建立
{
    setState(kConnected);
    channel_->tie(shared_from_this()); // channel拿着tcpconnecton的弱智能指针
    channel_->enableReading(); //向poller注册epollin事件

    //新连接建立 执行回调
    connectionCallback_(shared_from_this());
}
void TcpConnection::connectDestroyed() // 连接销毁
{
    if (state_ == kConnected)
    {
        setState(kDisconnected);
        channel_->disableAll();
        connectionCallback_(shared_from_this());
    }
    channel_->remove();
}


void TcpConnection::handleRead(Timestamp receiveTime)
{
    int saveErrno = 0;
    ssize_t n = inputBuffer_.readFd(channel_->fd(), &saveErrno);

    if (n > 0) {
        messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
    } else if (n == 0) {
        handleClose();
    } else {
        errno = saveErrno;
        handleError();
    }
}

void TcpConnection::handleWrite()
{
    if (channel_->isWriting()) {
       int saveErrno = 0;
       ssize_t n = outputBuffer_.writeFd(channel_->fd(), &saveErrno); 
       if (n > 0) {
           /// 看不懂
           outputBuffer_.retrieve(n); ///可写数据index向后移
           if (outputBuffer_.readableBytes() == 0) { /// 无可写区域了
               channel_->disableWriting(); // 别写了
               if (writeCompleteCallback_) {
                   // 唤醒loop_对应的thread线程
                   loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
               }
               if (state_ == kDisconnecting) {
                   shutdownInLoop();
               }
           }
       } 
    } 
    else //channel不可写
    {
        LOG_ERROR("channel don't write\n");
    }
}
void TcpConnection::handleClose()
{
    setState(kDisconnected);
    channel_->disableAll();

    TcpConnectionPtr connPtr(shared_from_this());
    connectionCallback_(connPtr);
    closeCallback_(connPtr);
}
void TcpConnection::handleError()
{
    int optval;
    int err = 0;
    socklen_t optlen = sizeof(optval);
    if (::getsockopt(channel_->fd(), SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) 
    {
        err = errno;
    }
    else
    {
        err = optval;
    }
    LOG_ERROR("TcpConnection::handleError() name %s -SO_ERROR:%d\n", name_.c_str(), err);
}

